﻿// <copyright file="PagedGrid.cs" company="Ucaya">
// </copyright>
// <author>Ucaya</author>

namespace Channel9Kinect
{
    #region

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;

    using Channel9Kinect.Input;

    #endregion

    [TemplatePart(Name = "GridHost", Type = typeof(FrameworkElement))]
    public class PagedGrid : Control
    {
        #region Constants and Fields

        /// <summary>
        ///   ItemTemplate depedency property.
        /// </summary>
        public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(PagedGrid), new PropertyMetadata((d, e) => ((PagedGrid)d).OnItemTemplateChanged((DataTemplate)e.OldValue, (DataTemplate)e.NewValue)));

        /// <summary>
        ///   PageIndex depedency property.
        /// </summary>
        public static readonly DependencyProperty PageIndexProperty = DependencyProperty.Register("PageIndex", typeof(int), typeof(PagedGrid), null);

        /// <summary>
        ///   PageIndexes depedency property.
        /// </summary>
        public static readonly DependencyProperty PageIndexesProperty = DependencyProperty.Register("PageIndexes", typeof(IEnumerable<int>), typeof(PagedGrid), null);

        /// <summary>
        ///   PageItemCount depedency property.
        /// </summary>
        public static readonly DependencyProperty PageItemCountProperty = DependencyProperty.Register("PageItemCount", typeof(double), typeof(PagedGrid), new PropertyMetadata((d, e) => ((PagedGrid)d).OnPageItemCountChanged((double)e.OldValue, (double)e.NewValue)));

        /// <summary>
        ///   PageSize depedency property.
        /// </summary>
        public static readonly DependencyProperty PageSizeProperty = DependencyProperty.Register("PageSize", typeof(int), typeof(PagedGrid), new PropertyMetadata(-1, (d, e) => ((PagedGrid)d).OnPageSizeChanged((int)e.OldValue, (int)e.NewValue)));

        /// <summary>
        ///   PagedCollection depedency property.
        /// </summary>
        public static readonly DependencyProperty PagedCollectionProperty = DependencyProperty.Register("PagedCollection", typeof(IPagedCollection), typeof(PagedGrid), new PropertyMetadata((d, e) => ((PagedGrid)d).OnPagedCollectionChanged((IPagedCollection)e.OldValue, (IPagedCollection)e.NewValue)));

        /// <summary>
        ///   Source depedency property.
        /// </summary>
        public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(IPagedCollectionSource), typeof(PagedGrid), new PropertyMetadata((d, e) => ((PagedGrid)d).OnSourceChanged((IPagedCollectionSource)e.OldValue, (IPagedCollectionSource)e.NewValue)));

        /// <summary>
        ///   Transition depedency property.
        /// </summary>
        public static readonly DependencyProperty TransitionProperty = DependencyProperty.Register("Transition", typeof(string), typeof(PagedGrid), new PropertyMetadata("DefaultTransition"));

        #endregion

        #region Constructors and Destructors

        static PagedGrid()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(PagedGrid), new FrameworkPropertyMetadata(typeof(PagedGrid)));
        }

        public PagedGrid()
        {
            this.DefaultStyleKey = typeof(PagedGrid);

            this.PageNext = new Command(() =>
                {
                    this.PagedCollection.MoveToNextPage();
                }, () => this.PagedCollection != null && this.PagedCollection.CanMoveNext);

            this.PagePrevious = new Command(() =>
                {
                    this.PagedCollection.MoveToPreviousPage();
                }, () => this.PagedCollection != null && this.PagedCollection.CanMovePrevious);

            this.GoToPage = new Command<int>((i) =>
                {
                    this.PagedCollection.MoveToPage(i);
                });
        }

        #endregion

        #region Public Properties

        public Command<int> GoToPage { get; private set; }

        public DataTemplate ItemTemplate
        {
            get
            {
                return (DataTemplate)this.GetValue(ItemTemplateProperty);
            }

            set
            {
                this.SetValue(ItemTemplateProperty, value);
            }
        }

        public int PageIndex
        {
            get
            {
                return (int)this.GetValue(PageIndexProperty);
            }

            private set
            {
                this.SetValue(PageIndexProperty, value);
            }
        }

        public IEnumerable<int> PageIndexes
        {
            get
            {
                return (IEnumerable<int>)this.GetValue(PageIndexesProperty);
            }

            set
            {
                this.SetValue(PageIndexesProperty, value);
            }
        }

        public double PageItemCount
        {
            get
            {
                return (double)this.GetValue(PageItemCountProperty);
            }

            set
            {
                this.SetValue(PageItemCountProperty, value);
            }
        }

        public Command PageNext { get; private set; }

        public Command PagePrevious { get; private set; }

        public int PageSize
        {
            get
            {
                return (int)this.GetValue(PageSizeProperty);
            }

            set
            {
                this.SetValue(PageSizeProperty, value);
            }
        }

        public IPagedCollection PagedCollection
        {
            get
            {
                return (IPagedCollection)this.GetValue(PagedCollectionProperty);
            }

            set
            {
                this.SetValue(PagedCollectionProperty, value);
            }
        }

        public Command<FrameworkElement> RefreshPageSize { get; private set; }

        public IPagedCollectionSource Source
        {
            get
            {
                return (IPagedCollectionSource)this.GetValue(SourceProperty);
            }

            set
            {
                this.SetValue(SourceProperty, value);
            }
        }

        public string Transition
        {
            get
            {
                return (string)this.GetValue(TransitionProperty);
            }

            set
            {
                this.SetValue(TransitionProperty, value);
            }
        }

        #endregion

        #region Methods

        /// <summary>
        ///   handles the ItemTemplateProperty changes.
        /// </summary>
        /// <param name = "oldValue">The old value.</param>
        /// <param name = "newValue">The new value.</param>
        private void OnItemTemplateChanged(DataTemplate oldValue, DataTemplate newValue)
        {
        }

        /// <summary>
        ///   handles the PageItemCountProperty changes.
        /// </summary>
        /// <param name = "oldValue">The old value.</param>
        /// <param name = "newValue">The new value.</param>
        private void OnPageItemCountChanged(double oldValue, double newValue)
        {
        }

        /// <summary>
        ///   handles the PageSizeProperty changes.
        /// </summary>
        /// <param name = "oldValue">The old value.</param>
        /// <param name = "newValue">The new value.</param>
        private void OnPageSizeChanged(int oldValue, int newValue)
        {
            if (this.PagedCollection != null)
            {
                if (!this.PagedCollection.IsPageChanging)
                {
                    this.PagedCollection.PageSize = newValue;
                    this.PageIndexes = Enumerable.Range(0, this.PagedCollection.PageSize == 0 ? 0 : (int)Math.Ceiling((double)this.PagedCollection.TotalItemCount / this.PagedCollection.PageSize)).ToArray();
                }
            }
            else
            {
                this.RefreshPage();
            }
        }

        /// <summary>
        ///   handles the PagedCollectionProperty changes.
        /// </summary>
        /// <param name = "oldValue">The old value.</param>
        /// <param name = "newValue">The new value.</param>
        private void OnPagedCollectionChanged(IPagedCollection oldValue, IPagedCollection newValue)
        {
            if (oldValue != null)
            {
                oldValue.PageChanging -= this.PagedCollectionPageChanging;
                oldValue.PageChanged -= this.PagedCollectionPageChanged;
                oldValue.PageChangedCancelled += this.PagedCollectionPageChangedCancelled;
                oldValue.PageChangedException += this.PagedCollectionPageChangedCancelled;
            }

            if (newValue != null)
            {
                newValue.PageChanging += this.PagedCollectionPageChanging;
                newValue.PageChanged += this.PagedCollectionPageChanged;
                newValue.PageChangedCancelled += this.PagedCollectionPageChangedCancelled;
                newValue.PageChangedException += this.PagedCollectionPageChangedCancelled;
            }
        }

        /// <summary>
        ///   handles the SourceProperty changes.
        /// </summary>
        /// <param name = "oldValue">The old value.</param>
        /// <param name = "newValue">The new value.</param>
        private void OnSourceChanged(IPagedCollectionSource oldValue, IPagedCollectionSource newValue)
        {
            IsBusyExtensions.SetBusyCount(this, 0);

            if (oldValue != null)
            {
                oldValue.RecreateCollection -= this.pageCollectionSourceRecreateCollection;
            }

            if (newValue != null)
            {
                newValue.RecreateCollection += this.pageCollectionSourceRecreateCollection;
            }

            this.RefreshPage();
        }

        private void PagedCollectionPageChanged(object sender, EventArgs e)
        {
            this.PageItemCount = this.PagedCollection.Page.OfType<object>().Count();
            if (this.PagedCollection.PageSize != this.PageSize)
            {
                this.PagedCollection.PageSize = this.PageSize;
            }
            this.PageIndex = this.PagedCollection.PageIndex;
            this.PageIndexes = Enumerable.Range(0, (int)Math.Ceiling((double)this.PagedCollection.TotalItemCount / this.PagedCollection.PageSize)).ToArray();
            this.SetBusy(false);
            CommandManager.InvalidateRequerySuggested();
        }

        private void PagedCollectionPageChangedCancelled(object sender, EventArgs e)
        {
            this.SetBusy(false);
            CommandManager.InvalidateRequerySuggested();
        }

        private void PagedCollectionPageChanging(object sender, PageChangingEventArgs e)
        {
            this.SetBusy(true);
            this.PageItemCount = this.PageSize;

            if (e.NewPageIndex > this.PagedCollection.PageIndex)
            {
                this.Transition = "PageNext";
            }
            else
            {
                this.Transition = "PagePrevious";
            }
        }

        private void RefreshPage()
        {
            IsBusyExtensions.SetBusyCount(this, 0);

            if (this.PageSize <= 0 || this.Source == null)
            {
                return;
            }

            this.SetBusy(true);

            this.PagedCollection = null;
            CommandManager.InvalidateRequerySuggested();

            //Get Paged Video
            this.Source.GetPagedCollection((collection) =>
                {
                    try
                    {
                        this.PagedCollection = collection;
                    }
                    catch
                    {
                        // Retry, Bug element is already the child of another
                        this.PagedCollection = null;
                        this.PagedCollection = collection;
                    }
                    if (this.PagedCollection != null)
                    {
                        this.PagedCollection.MoveToFirstPage();
                    }
                    CommandManager.InvalidateRequerySuggested();
                    this.SetBusy(false);
                }, _ =>
                    {
                        this.PagedCollection = null;
                        this.ShowError(_);
                        CommandManager.InvalidateRequerySuggested();
                        this.SetBusy(false);
                    }, this.PageSize);
        }

        private void ShowError(Exception ex)
        {
        }

        private void pageCollectionSourceRecreateCollection(object sender, EventArgs e)
        {
            this.RefreshPage();
        }

        #endregion
    }
}